using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

using Atomic.Libraries;
using Atomic.Structures;

namespace Atomic.Interfaces.Atat
{
	public static class AtatSuperStructureGenerator
	{
		/// <summary>
		/// Generates a superstructure by specifying the minimum distance of between mirrored images of the same original atom.
		/// The structure is generated using the ATAT fitfc tool.
		/// </summary>
		public static Structure Generate(Structure structure, double distance)
		{
			if (distance <= 0.0)
			{
				throw new ArgumentException("Minimum distance must be positive.");
			}

			foreach (Site site in structure.Sites)
			{
				if (site.Atom is PlaceholderAtom)
				{
					// Use ReplacementAtom here.
					throw new ArgumentException("Placeholders aren't supported.");
				}
			}

			using (TempDirectory temp = new TempDirectory())
			{
				structure.Write(temp.Directory.AddFile("str.out"));

				// Start fitfc process. IEEE 754 double-precision has up to 17 significant digits.
				Process p = new Process();
				p.StartInfo.FileName = "fitfc";
				p.StartInfo.Arguments = string.Format("-si=str.out -sr=str.out -er={0:r} -nrr -ns=1 -ms=0 -dr=0 -sig=17", distance);
				p.StartInfo.WorkingDirectory = temp.Directory.FullName;
				p.StartInfo.UseShellExecute = false;
				p.StartInfo.RedirectStandardOutput = true;
				p.StartInfo.RedirectStandardError = true;

				p.Start();
				p.WaitForExit();

				if (p.ExitCode != 0)
				{
					throw new Exception(string.Format("Running fitfc failed: {0}", p.StandardError.ReadToEnd()));
				}

				// Scan output directories.
				return Scan(temp.Directory);
			}
		}

		/// <summary>
		/// Generates a superstructure by specifying the minimum distance of between mirrored images of the same original atom.
		/// The structure is generated using the ATAT fitfc tool.
		/// </summary>
		public static Structure GenerateMapped(Structure structure, double distance)
		{
			return structure.Repeat(LatticeRepeatMatrix.Create(structure, Generate(structure, distance)));
		}

		private static Structure Scan(DirectoryInfo directory)
		{
			foreach (DirectoryInfo d1 in directory.GetDirectories())
			{
				if (d1.Name.StartsWith("vol_"))
				{
					foreach (DirectoryInfo d2 in d1.GetDirectories())
					{
						if (d2.Name.StartsWith("p+") && d2.Name.EndsWith("_0"))
						{
							Structure s = Structure.Read(d2.AddFile("str.out"));
							return new Structure(s.Basis, s.Lattice, s.Sites);
						}
					}
				}
			}

			throw new Exception("Couldn't find generated superstructure.");
		}

		[Serializable]
		private class ReplacementAtom : Atom
		{
			private int index;

			public ReplacementAtom(int index)
			{
				this.index = index;
			}

			public override bool Equals(Atom other)
			{
				return object.ReferenceEquals(this, other);
			}
			
			public override string Symbol
			{
				get
				{
					return string.Format("Xxx{0}", index);
				}
			}

			public override int Number
			{
				get
				{
					throw new NotImplementedException();
				}
			}

			public override double Mass
			{
				get
				{
					throw new NotImplementedException();
				}
			}

			public override string ElectronConfiguration
			{
				get
				{
					throw new NotImplementedException();
				}
			}
		}
	}
}
